package mmc.cell;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;

import org.lwjgl.opengl.GL11;

import mmc.Constants;
import mmc.EngineObject;
import mmc.gl.BackgroundLoader;
import mmc.gl.shader.CellShader;
import mmc.resource.BlockTextures;
import static mmc.util.MyMath.floor;
import static org.lwjgl.opengl.GL11.glDisableClientState;
import static org.lwjgl.opengl.GL11.glEnableClientState;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.glBindBuffer;

public class ClientCellBunch{

	static float renderLoopLoad = 0;
	static float maxRenderLoopLoad = 1;//50/8;
	
	private Map<CellPosition, ClientCell> cells = new TreeMap<CellPosition, ClientCell>();
	private Collection<ClientCell> cellsValues = new ArrayList<ClientCell>(1);
	private BackgroundLoader backgroundLoader;
	
	public static ClientCellBunch cellBunch;
	
	public static BlockTextures blockTexture;
	
	public ClientCellBunch(BackgroundLoader backgroundLoader) {
		this.backgroundLoader = backgroundLoader;
		cellBunch = this;
		blockTexture = new BlockTextures();
		CellShader.get().setTexture(blockTexture);
	}

	public void render(EngineObject observer) {
		blockTexture.bind();
		
		CellShader.get().use();
		
		glEnableClientState(GL11.GL_VERTEX_ARRAY);
		glEnableClientState(GL11.GL_NORMAL_ARRAY);
		glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		
		for(ClientCell c : cellsValues){
			c.render(observer);
		}
		
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
		
		glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		glDisableClientState(GL11.GL_NORMAL_ARRAY);
		glDisableClientState(GL11.GL_VERTEX_ARRAY);
		
		CellShader.useDefault();
	}
	/*
	public void update(float dt) {
		for(ClientCell c : cellsValues){
			c.update(dt);
		}
	}*/
	
	public void updateBuffers() {
		for(ClientCell c : cells.values()){
			c.updateBuffers();
			
			if(renderLoopLoad > maxRenderLoopLoad){
				renderLoopLoad = 0;
				return;
			}
		}
	}

	public void putCell(ClientCell c){
		ClientCell current = cells.get(c.pos);
		if(current==null){
			cells.put(c.pos, c);
			c.notifyCellContentChanged();
			cellsValues = cells.values();
			if(Constants.CELL_BACKGROUND_LOADING)
				backgroundLoader.addCell(c);
		}else{
			current.block = c.block;
			current.solid = c.solid;
			current.notifyCellContentChanged();
		}
	}
	
	public ClientCell getCell(int x, int y, int z){
		return cells.get(new CellPosition(x,y,z));
	}
	public ClientCell getCellSafe(int x, int y, int z){
		return cells.get(new CellPosition(
				x&Constants.CELL_MASK,
				y&Constants.CELL_MASK,
				z&Constants.CELL_MASK));
	}
	
	public byte getContainingBlock(int ix, int iy, int iz){
		ClientCell c = getCellSafe(ix, iy, iz);
		if(c==null)
			return 0;
		else{
			if(c.solid==ServerCell.CELL_NOT_COMPRESSED)
				return c.block[(ix - c.pos.x)*Constants.CELL_SIZE2+(iy - c.pos.y)*Constants.CELL_SIZE+(iz - c.pos.z)];
			else
				return (byte)c.solid;
		}
	}
	
	public byte getContainingBlock(float x, float y, float z){
		int ix = floor(x),
	        iy = floor(y),
	        iz = floor(z);
		
		ClientCell c = getCellSafe(ix, iy, iz);
		if(c==null)
			return 0;
		else{
			if(c.solid==ServerCell.CELL_NOT_COMPRESSED)
				return c.block[(ix - c.pos.x)*Constants.CELL_SIZE2+(iy - c.pos.y)*Constants.CELL_SIZE+(iz - c.pos.z)];
			else
				return (byte)c.solid;
		}
	}
	
	public void unloadCell(CellPosition pos){
		ClientCell c = cells.remove(pos);
		c.compress();
		if(Constants.CELL_BACKGROUND_LOADING)
			backgroundLoader.removeCell(c);
		cellsValues = cells.values();
	}
	
	public Map<CellPosition, ClientCell> getCells(){
		return cells;
	}
	
	public float clearLoad(){
		try{
			return renderLoopLoad;
		}finally{
			renderLoopLoad = 0;
		}
	}
}
